home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Environments / PowerMacOberon feb96 / Source / TCP.Mod (.txt) < prev    next >
Encoding:
Oberon Text  |  1995-12-20  |  67.0 KB  |  1,498 lines  |  [TEXT/.Ob4]

  1. Syntax10.Scn.Fnt
  2. Syntax10b.Scn.Fnt
  3. Syntax10i.Scn.Fnt
  4. MODULE TCP;
  5. (** Implementation of TCP for PowerMac Oberon by Daniel Scherer, scherer@tik.ee.ethz.ch, 15.06.1994 / 24.04.1995. *)(*
  6. Copyright (c) Computer Engineering and Networks Laboratory (TIK), ETH Zurich, 1994, 1995.
  7. Restricted Freeware: This software should not be passed on to third parties, but may be requested free of charge from the author.
  8. It should not be used for commercial purposes.
  9. It is provided on an "as is" basis without warranty of any kind; we shall not be liable for any damages.
  10. Credits: Based on TCP for Oberon for Windows by Matthias Hausner
  11. and an implementation of TCP for MacOberon by Stefan Loetscher.
  12. According to definition TCP.Text by Institute for Computer Systems, ETH Zurich, 25.07.1994.
  13. Small deviations from and extensions to that definition are marked with "Mac:" or "Mac only:".
  14. Standard interface for a simple, synchronous interface to the TCP/IP protocol allowing the implementation
  15. of Oberon TCP/IP clients and servers.
  16. Asynchronous implementation with finalization for Power Macintosh running a recent PowerMac Oberon System (V4 or System 3);
  17. includes Domain Name Resolving (DNR).
  18. Requires MacTCP from Apple or System >= 7.5 which includes MacTCP.
  19. System extension ZapTCP from Apple is recommended.
  20. Information about MacTCP is in MacTCP Programmer's Guide.
  21. DNR is implemented following Apple's example C program "dnr.c".
  22. Original definition of TCP is in document rfc793 (ftp from ftp.ethz.ch or nic.switch.ch). *)
  23. IMPORT SYSTEM, Sys, Kernel, Modules, T := Texts, O := Oberon;
  24. CONST
  25. (* ---------- TCP Constants ---------- *)
  26.     fsCurPerm = 0;    (* IO Driver Permissions used by PBOpen *)
  27.     inProgress = 1;        (* I/O in progress; still in the queue of the DeviceManager *)
  28.     controlErr = -17;        (* Errors of the Device Manager *)
  29.     statusErr = -18;
  30.     readErr = -19;
  31.     writeErr = -20;
  32.     badUnitErr = -21;
  33.     unitEmptyErr =-22;
  34.     openErr = -23;
  35.     abortErr = -27;
  36.     notOpenErr = -28;
  37.     ioErr = -36;
  38.     DeviceID  = ".IPP";            (* Device id of MacTCP *)
  39.     RecBufferSize = 16384;    (* minimum recommended TCP buffer size  *)
  40.     RWdsEntries = 1;            (* number of entries in the RDS/WDS *)
  41. (* Commands of MacTCP; see MacTCP manual *)
  42.     csPBOpen = 1;                        (* used by WriteRes; any number different from the following TCP commands *)
  43.     csIPctlGetAddr = 15;
  44.     csUDPMaxMTUSize = 25;
  45.     csTCPCreate = 30;
  46.     csTCPPassiveOpen = 31;
  47.     csTCPActiveOpen = 32;
  48.     csTCPSend = 34;
  49.     csTCPNoCopyRcv = 35;
  50.     csTCPBfrReturn = 36;
  51.     csTCPRcv = 37;
  52.     csTCPClose = 38;
  53.     csTCPAbort = 39;
  54.     csTCPStatus = 40;
  55.     csTCPRelease = 42;
  56.     csTCPGlobalInfo = 43;
  57. (* TCP Connection States returned by TCPStatus command; see MacTCP manual *)
  58.     Closed = 0;
  59.     Listening = 2;
  60.     SYNrcv = 4;
  61.     SYNsnd = 6;
  62.     Established = 8;
  63.     FINWait1 = 10;
  64.     FINWait2 = 12;
  65.     CloseWait = 14;
  66.     Closing = 16;
  67.     LastAck = 18;
  68.     TimeWait = 20;
  69. (* MacTCP Result Codes/Errors; see MacTCP manual *)
  70.     noErr                    =        0;
  71.     ipBadLapErr            =        -23000;            (* bad network configuration *)
  72.     ipBadCnfgErr            =        -23001;        (* bad IP configuration error *)
  73.     ipNoCnfgErr            =        -23002;            (* missing IP or LAP configuration error *)
  74.     ipLoadErr                =        -23003;            (* error in MacTCP load *)
  75.     ipBadAddr                =        -23004;            (* error in getting address *)
  76.     connectionClosing        =    -23005;            (* connection is closing *)
  77.     invalidLength            =        -23006;    (* the total amount of data described by the WDS was either 0 or > 65535 bytes *) 
  78.     connectionExists            =    -23007;        (* request conflicts with existing connection *)
  79.     connectionDoesntExist    =    -23008;        (* connection does not exist *)
  80.     insufficientResources    =    -23009;            (* insufficient resources to perform request *)
  81.     invalidStreamPtr        =    -23010;            (* the specified TCP or UDP stream is not open *)
  82.     streamAlreadyOpen    =    -23011;          (* an open stream is already using this receive buffer area *)                  
  83.     connectionTerminated    =    -23012;        (* the TCP connection was broken *)
  84.     invalidBufPtr        =        -23013;            (* the receive buffer area pointer was NIL *)
  85.     invalidRWDS            =        -23014;        (* the RDS refers to receive buffers not owned by the user; the WDS was NIL *)
  86.     openFailed            =        -23015;        (* the connection came halfway up and then failed *)
  87.     commandTimeout    =        -23016;    (* the specified command action was not completed in the specified time period *)
  88.     duplicateSocket        =        -23017;    (* a TCP connection already exists between the specified addresses and ports *)
  89. (* MacTCP Result Codes/Error codes from internal IP functions and DNR; see MacTCP manual *)
  90.     ipDontFragErr        =        -23032;            (* Packet too large to send w/o fragmenting *)
  91.     ipDestDeadErr        =        -23033;            (* destination not responding *)
  92.     ipNoFragMemErr    =        -23036;            (* no memory to send fragmented pkt *)
  93.     ipRouteErr            =        -23037;            (* can't route packet off-net *)
  94.     nameSyntaxErr     =        -23041;                (* etc...  see manual *)
  95.     cacheFault            =        -23042;
  96.     noResultProc        =        -23043;
  97.     noNameServer    =            -23044;
  98.     authNameErr        =        -23045;
  99.     noAnsErr            =        -23046;
  100.     dnrErr                =        -23047;
  101.     outOfMemory        =        -23048;
  102. (* ---------- DNR Constants ---------- *)
  103.     DNRTimeout = 10; (* secs *)
  104.     OPENRESOLVER = 1;
  105.     CLOSERESOLVER = 2;
  106.     STRTOADDR = 3;
  107.     ADDRTONAME = 6;
  108. (* ---------- Implementation Constants ---------- *)
  109.     Sec = 1000;    (* Set this factor so that Oberon.Time () DIV Sec = time in 1 second units. *)
  110.     Done* = 0; NotDone* = 1; Timeout* = 2; LocalPortInUse* = 3;
  111.             (** result codes for Connect (0,1,2,3), Listen (0,1,3), Accept (0,1) *)
  112.     AnyAdr* = 0; AnyPort* = 0;    (** any IP address; any TCP port *)
  113.     MaxStreams = 64;                    (* size of connTab and listTab *)
  114.     ULPTimeout = 5;                        (* ULP timeout in seconds *)
  115.     CmdTimeout = 5;                        (* command timeout in seconds *)
  116.     DefaultConnectTimeout = 5;        (* must be > 0; default Connect timeout in seconds *)
  117.     SyncTimeout = 0;                        (* Synchronous: indefinite timeout; ControlDev will call PBControlSync *)
  118.     CloseTimeout = 5;                        (* Close and Disconnect timeouts in seconds *)
  119.     SendRcvTimeout = 5;
  120.     StatusTimeout = 5;
  121.     ReleaseTimeout = 5;                    (* Release timeout in seconds *)
  122.     DefaultTimeout = 5;                    (* default timeout for other commands in seconds *)
  123. (* ---------- TCP Types ---------- *)
  124.     StreamPtr = LONGINT;
  125.     BufferPtr = LONGINT;
  126.     PBPtr = POINTER TO PB;        (* General Parameter Block of Device Manager call *)
  127.     PB = RECORD [Sys.align68K]
  128.         qLink: LONGINT;
  129.         qType: INTEGER;
  130.         ioTrap: INTEGER;
  131.         ioCmdAddr: LONGINT;
  132.         ioCompletion*: LONGINT;
  133.         ioResult*: INTEGER;
  134.         ioNamePtr*: LONGINT;
  135.         ioVRefNum*: INTEGER;
  136.         ioCRefNum*: INTEGER;
  137.     END;
  138.     PBOpenPtr = POINTER TO PBOpen;        (* Parameter Block of Device Manager PBOpen call *)
  139.     PBOpen = RECORD [Sys.align68K]
  140.         (PB)
  141.         dummy: SHORTINT;
  142.         ioPermissions*: SHORTINT;
  143.     END;
  144.     PBControlPtr = POINTER TO PBControl;        (* Parameter Block of Device Manager PBControl call *)
  145.     PBControl = RECORD [Sys.align68K]
  146.         (PB)
  147.         csCode*: INTEGER;
  148.     END;
  149.     RWDSPtr = POINTER TO RWDS;
  150.     RWDS = RECORD [Sys.align68K]                (* Read/Write Data Structure; 1 entry *)
  151.                 bufLen*: INTEGER;
  152.                 buf*: BufferPtr;                    
  153.                 zero*: INTEGER;
  154.             END;
  155.     GetAddrPBPtr = POINTER TO GetAddrPB;    (* Data Structure of the MacTCP get my IP address command *)
  156.     GetAddrPB = RECORD [Sys.align68K]
  157.                             (PBControl);
  158.                             ourAddr*: LONGINT;
  159.                             ourMask*: LONGINT;
  160.                         END;
  161.     UdpMtuPBPtr = POINTER TO UdpMtuPB;    (* Data Structure of the MacTCP UDP maximum transfer unit size command *)
  162.     UdpMtuPB = RECORD [Sys.align68K]
  163.                         (PBControl);
  164.                         dummy: LONGINT;
  165.                         maxDataSize*: INTEGER;
  166.                         remAdr*: LONGINT;
  167.                         usrDataPtr*: LONGINT;
  168.                     END;
  169.     CreatePBPtr = POINTER TO CreatePB;        (* Data Structure of the MacTCP create, release commands *)
  170.     CreatePB = RECORD [Sys.align68K]
  171.                             (PBControl);    
  172.                             streamPtr*: StreamPtr;
  173.                             bufferPtr*: BufferPtr;
  174.                             bufferLen*: LONGINT;
  175.                             ASRPtr*: LONGINT;
  176.                             usrDataPtr*: LONGINT;
  177.                         END;
  178.     ClosePBPtr = POINTER TO ClosePB;        (* Data Structure of the MacTCP close command *)
  179.     ClosePB = RECORD [Sys.align68K]
  180.                             (PBControl);    
  181.                             streamPtr*: StreamPtr;
  182.                             ULPtoVal*: SHORTINT;
  183.                             ULPtoAct*: SHORTINT;
  184.                             validity*: SHORTINT;
  185.                             usrDataPtr*: LONGINT;
  186.                         END;
  187.     AbortPBPtr = POINTER TO AbortPB;        (* Data Structure of the MacTCP abort command *)
  188.     AbortPB = RECORD [Sys.align68K]
  189.                             (PBControl);    
  190.                             streamPtr*: StreamPtr;
  191.                             usrDataPtr*: LONGINT;
  192.                         END;
  193.     OpenPBPtr = POINTER TO OpenPB;        (* Data Structure of the MacTCP open commands *)
  194.     OpenPB = RECORD [Sys.align68K]
  195.                         (PBControl);
  196.                         streamPtr*: StreamPtr;
  197.                         ULPtoVal*: SHORTINT;
  198.                         ULPtoAct*: SHORTINT;
  199.                         validity*: SHORTINT;
  200.                         cmdto*: SHORTINT;
  201.                         remAdr*: LONGINT;
  202.                         remPort*: INTEGER;
  203.                         localAdr*: LONGINT;
  204.                         localPort*: INTEGER;
  205.                         serviceType*: SHORTINT;
  206.                         precedence*: SHORTINT;
  207.                         dontfrag*: SHORTINT;
  208.                         livetime*: SHORTINT;
  209.                         security*: SHORTINT;
  210.                         IPoptcount*: SHORTINT;
  211.                         IPoptions*: ARRAY 10 OF LONGINT;
  212.                         usrDataPtr*: LONGINT;
  213.                     END;
  214.     StatusPBPtr =POINTER TO StatusPB;        (* Data Structure of the MacTCP status command *)
  215.     StatusPB = RECORD [Sys.align68K]
  216.                         (PBControl);
  217.                         streamPtr*: StreamPtr;
  218.                         ULPtoVal*: SHORTINT;
  219.                         ULPtoAct*: SHORTINT;
  220.                         dummy: LONGINT;
  221.                         remAdr*: LONGINT;                    
  222.                         remPort*: INTEGER;
  223.                         localAdr*: LONGINT;
  224.                         localPort*: INTEGER;
  225.                         serviceType*: SHORTINT;
  226.                         precedence*: SHORTINT;
  227.                         connState*: SHORTINT;
  228.                         dummy1: SHORTINT;
  229.                         sndWindow*: INTEGER;
  230.                         rcvWindow*: INTEGER;
  231.                         unacknowledged*: INTEGER;
  232.                         unreadData*: INTEGER;
  233.                         secOptionPtr*: LONGINT;
  234.                         sndUnacknowledged*: LONGINT;
  235.                         sndNext*: LONGINT;
  236.                         congWindow*: LONGINT;
  237.                         rcvNext*: LONGINT;
  238.                         smrtdelay*: LONGINT;
  239.                         lsrtdelay*: LONGINT;
  240.                         maxSegSize*: LONGINT;
  241.                         statisticsPtr*: LONGINT;
  242.                         usrDataPtr*: LONGINT;
  243.                     END;
  244.     SendPBPtr = POINTER TO SendPB;    (* Data Structure of the MacTCP send command *)
  245.     SendPB = RECORD [Sys.align68K]
  246.                         (PBControl);
  247.                         streamPtr*: StreamPtr;
  248.                         ULPtoVal*: SHORTINT;
  249.                         ULPtoAct*: SHORTINT;
  250.                         validity*: SHORTINT;
  251.                         pushflag*: SHORTINT;
  252.                         urgentflag*: SHORTINT;
  253.                         dummy: SHORTINT;
  254.                         wdsPtr*: RWDSPtr;
  255.                         reserved1: LONGINT;
  256.                         reserved2: INTEGER;
  257.                         usrDataPtr*: LONGINT;
  258.                     END;
  259.     RcvPBPtr = POINTER TO RcvPB;    (* Data Structure of the MacTCP receive, nocopyreceive and bufferreturn commands *)
  260.     RcvPB = RECORD [Sys.align68K]
  261.                         (PBControl);
  262.                         streamPtr*: StreamPtr;
  263.                         cmdto*: SHORTINT;
  264.                         dummy: SHORTINT;
  265.                         markflag*: SHORTINT;
  266.                         urgentflag*: SHORTINT;
  267.                         rcvBufPtr*: LONGINT;
  268.                         rcvBufLen*: INTEGER;
  269.                         rdsPtr*: RWDSPtr;
  270.                         numOfEntries*: INTEGER;
  271.                         reserved1: INTEGER;
  272.                         usrDataPtr*: LONGINT;
  273.                     END;
  274.     GlobalInfoPBPtr = POINTER TO GlobalInfoPB;
  275.     GlobalInfoPB = RECORD [Sys.align68K]
  276.                                 (PBControl);
  277.                                 dummy: LONGINT;
  278.                                 TCPparamPtr*: LONGINT;
  279.                                 TCPstatsPtr*: LONGINT;
  280.                                 CDBtablePtr*: LONGINT;
  281.                                 usrDataPtr*: LONGINT;
  282.                                 maxTCPconnections*: INTEGER;
  283.                             END;
  284. (* ---------- DNR Types ---------- *)
  285.     Handle = LONGINT;
  286.     PBGetFInfoPtr = POINTER TO PBGetFInfoPB;
  287.     PBGetFInfoPB = RECORD [Sys.align68K] (* Inside Mac II-105, II-115 *)
  288.         dummy0: ARRAY 6 OF INTEGER;
  289.         ioCompletion*: LONGINT;
  290.         ioResult*: INTEGER;
  291.         ioNamePtr*: POINTER TO ARRAY 256 OF CHAR;
  292.         ioVRefNum*: INTEGER;
  293.         ioCRefNum*: INTEGER;
  294.         dummy1: INTEGER;
  295.         ioFDirIndex*: INTEGER;
  296.         dummy2: INTEGER;
  297.         fdType*, fdCreator*: LONGINT; (*ARRAY 4 OF CHAR*)
  298.         dummy3: ARRAY 4 OF INTEGER;
  299.         ioDirID*: LONGINT; (* ioFlNum *)
  300.         dummy4: ARRAY 14 OF INTEGER;
  301.     END;
  302.     hostInfoPtr = POINTER TO hostInfo;
  303.     hostInfo = RECORD [Sys.align68K]
  304.         rtnCode: LONGINT;
  305.         cname: ARRAY 256 OF CHAR;
  306.         addr: ARRAY 4 OF LONGINT; (* A host may have up to 4 IP-addresses *)
  307.     END;
  308.     StrPtr = POINTER TO ARRAY 256 OF CHAR;
  309.     (*ResultProc = PROCEDURE (rtnStruct: hostInfoPtr; userDataPtr: StrPtr);*)
  310.     DNRProc = Sys.UniversalProcPtr;
  311.     OpenResProc = PROCEDURE (dnr: Sys.UniversalProcPtr; procInfo: LONGINT; index: LONGINT; filename: StrPtr): INTEGER; 
  312.     CloseResProc = PROCEDURE (dnr: Sys.UniversalProcPtr; procInfo: LONGINT; index: LONGINT): INTEGER;
  313.     StrToAddrProc = PROCEDURE (dnr: Sys.UniversalProcPtr; procInfo: LONGINT; index: LONGINT; hostName: StrPtr;
  314.                                                 rtnStruct: hostInfoPtr; resultproc: LONGINT(*ResultProc*); userDataPtr: StrPtr): INTEGER;
  315.     AddrToNameProc = PROCEDURE (dnr: Sys.UniversalProcPtr; procInfo: LONGINT; index: LONGINT; addr: LONGINT;
  316.                                                 rtnStruct: hostInfoPtr; resultproc: LONGINT(*ResultProc*); userDataPtr: StrPtr): INTEGER;
  317. (* ---------- Implementation Types ---------- *)
  318.     IpAdr* = LONGINT;
  319.     Stream = POINTER TO StreamDesc;
  320.     StreamDesc = RECORD
  321.         CreatePb: CreatePBPtr;    (* .streamPtr identifies a TCP stream created by TCPCreate *)
  322.         OpenPb: OpenPBPtr;
  323.         ClosePb: ClosePBPtr;
  324.         StatusPb: StatusPBPtr;
  325.         SendPb: SendPBPtr;
  326.         RcvPb: RcvPBPtr;
  327.             (* These parameter blocks are allocated only once per stream and reused to avoid heap overflow
  328.                 (must check StatusPb, SendPb, RcvPb if still inProgress before reusing; OpenPb, ClosePb used only once);
  329.                 references to all PBs must be kept until stream has been released asynchronously *)
  330.         res: INTEGER;                (* MacTCP result code *)
  331.     END;
  332.     Connection* = POINTER TO ConnectionDesc;
  333.     ConnectionDesc* = RECORD
  334.         pbs: Stream;
  335.         id-: LONGINT; (** unique id supplied by TCP to obtain the Connection with TCP.ThisConnection(id);
  336.                 application should keep reference to Connection while Connection is in use, otherwise Connection will be closed
  337.                 by finalization; if a view is related to the Connection, the view-descriptor should contain the reference to the
  338.                 Connection and the view's model should only keep the Connection's id (and get the Connection with
  339.                 TCP.ThisConnection(id)), so that the Connection will be finalized when the view no longer exists (has been closed). *)
  340.         radr-: IpAdr; (** Mac only: connection's remote address *)
  341.         lport-: INTEGER; (** Mac only: connection's local port *)
  342.         rport-: INTEGER; (** Mac only: connection's remote port *)
  343.     END;
  344.     Listener* = POINTER TO ListenerDesc;
  345.     ListenerDesc* = RECORD (** Mac only: exported *)
  346.         pbs: Stream;
  347.         id-: LONGINT; (** Mac only: unique id supplied by TCP to obtain the Listener with TCP.ThisListener(id) *)
  348.         lport: INTEGER;
  349.         listening: BOOLEAN;    (* set by Listen and Accept and used by Requested and Accept *)
  350.     END;
  351.     Str255 = ARRAY 256 OF CHAR;            (* used by StrToStr255 and InitDriver *)
  352. (* ---------- DNR Vars ---------- *)
  353.     addrCache: IpAdr;
  354.     shortNameCache, longNameCache: ARRAY 64 OF CHAR;
  355.     dnr: DNRProc;
  356.     codeHndl: Handle;
  357.     oberonIoVRefNum: INTEGER;
  358.     oberonDirID: LONGINT;
  359.     gRtnStruct: hostInfoPtr;
  360.     gStrToAddrDone: INTEGER;
  361.     gSetVolDone: BOOLEAN;
  362.     gResProc: ARRAY 9 OF INTEGER;
  363.     (* MacFiles *)
  364.     PBHGetFInfoSync: PROCEDURE (paramBlock: PBGetFInfoPtr): INTEGER;
  365.     HGetVol: PROCEDURE (volName: LONGINT; VAR vRefNum: INTEGER; VAR dirID: LONGINT): INTEGER;
  366.     HSetVol: PROCEDURE (volName: LONGINT; vRefNum: INTEGER; dirID: LONGINT): INTEGER;
  367.     (* MacMemory *)
  368.     DisposeHandle: PROCEDURE (h: Handle);
  369.     HLock: PROCEDURE (h: Handle);
  370.     HUnlock: PROCEDURE (h: Handle);
  371.     (* MacMoreToolbox *)
  372.     OpenResFile: PROCEDURE (fileName: Str255): INTEGER;
  373.     CloseResFile: PROCEDURE (refNum: INTEGER);
  374.     ResError: PROCEDURE (): INTEGER;
  375.     GetIndResource: PROCEDURE (theType: LONGINT; index: INTEGER): Handle;
  376.     DetachResource: PROCEDURE (theResource: Handle);
  377.     (* New Inside Mac Toolbox Essentials *)
  378.     FindFolder: PROCEDURE (vRefNum: INTEGER; folderType: LONGINT; createFolder: BOOLEAN;
  379.                                             VAR foundVRefNum: INTEGER; VAR foundDirID: LONGINT): INTEGER;
  380. (* ---------- TCP Implementation Vars ---------- *)
  381.     PBOpenSync: PROCEDURE (p: PBOpenPtr): INTEGER;        (* Device Open *)
  382.     PBControlSync: PROCEDURE (p: PBControlPtr): INTEGER;        (* Device Control; communicates with TCP driver *)
  383.     PBControlAsync: PROCEDURE (p: PBControlPtr): INTEGER;        (* Asynchronous call of PBControl *)
  384.     NewPtr: PROCEDURE (size: LONGINT): LONGINT;    (* allocates new block of memory *)
  385.     DisposePtr: PROCEDURE (p: LONGINT);            (* disposes block of memory *)
  386.     ioCRefNum: INTEGER;    (* device reference number returned by PBOpen and used by all subsequent PBControl calls *)
  387.     maxMTUsize: INTEGER;    (* returned by UDPGetMTUSize *)
  388.     recBufferSize: LONGINT;    (* recommended buffer size for a stream *)
  389.     W: T.Writer;                    (* for error output *)
  390.     i: INTEGER;                    (* to initialize connTab and listTab *)
  391.     connCount, listCount: INTEGER;
  392.     connTab: ARRAY MaxStreams OF LONGINT; (* Connection, weak pointers to unreleased streams *)
  393.     listTab: ARRAY MaxStreams OF LONGINT; (* Listener, weak pointers to unreleased streams *)
  394.     res*: INTEGER; (** Done or NotDone, after any Read... or Write... call *)
  395.                         (* (set in TCPSend, TCPRcv (not yet in TCPNoCopyRcv); read in WriteBytes, and exported) *)
  396. (* ---------- DNR Procs ---------- *)
  397. PROCEDURE OpenOurRF (): INTEGER;
  398.     sysVRefNum: INTEGER; sysDirID: LONGINT;
  399.     pbGetFInfoPtr: PBGetFInfoPtr;
  400.     res: INTEGER;
  401. BEGIN
  402.     res := FindFolder (SYSTEM.VAL (INTEGER, 08000H) (*kOnSystemDisk*), 06D616373H (*kSystemFolderType = 'macs'*),
  403.                             FALSE, sysVRefNum, sysDirID);
  404.     IF res # noErr THEN RETURN -1 END;
  405.     NEW (pbGetFInfoPtr);
  406.     pbGetFInfoPtr.ioCompletion := 0;
  407.     NEW (pbGetFInfoPtr.ioNamePtr);
  408.     pbGetFInfoPtr.ioVRefNum := sysVRefNum;
  409.     pbGetFInfoPtr.ioDirID := sysDirID;
  410.     pbGetFInfoPtr.ioFDirIndex := 1;
  411.     LOOP
  412.         res := PBHGetFInfoSync (pbGetFInfoPtr);
  413.         IF res # noErr THEN RETURN -1 END;
  414.         IF (pbGetFInfoPtr.fdType = 063646576H (*"cdev"*)) & (pbGetFInfoPtr.fdCreator = 06D746370H (*"mtcp"*)) THEN
  415.             (* found the MacTCP driver file *)
  416.             res := Sys.HGetVol (0, oberonIoVRefNum, oberonDirID);
  417.             IF res # noErr THEN RETURN -1 END;
  418.             res := Sys.HSetVol (0, sysVRefNum, sysDirID);
  419.             IF res # noErr THEN RETURN -1 END;
  420.             gSetVolDone := TRUE;
  421.             RETURN OpenResFile (SYSTEM.VAL (Str255, pbGetFInfoPtr.ioNamePtr^)); (* if OK, returns file ref num > 0 *)
  422.         END;
  423.         INC (pbGetFInfoPtr.ioFDirIndex);
  424.         pbGetFInfoPtr.ioDirID := sysDirID;
  425.     END;
  426.     RETURN -1;
  427. END OpenOurRF;
  428. PROCEDURE OpenResolver (filename: ARRAY OF CHAR): INTEGER;
  429.     refnum, res: INTEGER;
  430.     open: OpenResProc;
  431.     codePtr, procInfo: LONGINT;
  432. BEGIN
  433.     IF dnr # NIL THEN RETURN noErr END;
  434.     gSetVolDone := FALSE;
  435.     refnum := OpenOurRF ();
  436.     codeHndl := GetIndResource (0646E7270H (*"dnrp"*), 1);
  437.     IF codeHndl = 0 THEN
  438.         IF gSetVolDone THEN res := Sys.HSetVol (0, oberonIoVRefNum, oberonDirID) END;
  439.         RETURN ResError ()
  440.     END;
  441.     DetachResource (codeHndl);
  442.     IF refnum # -1 THEN CloseResFile(refnum) END;
  443.     res := Sys.HSetVol (0, oberonIoVRefNum, oberonDirID);
  444.     HLock (codeHndl);
  445.     SYSTEM.GET (codeHndl, codePtr);
  446.     procInfo := 03A1H;
  447.     dnr := Sys.NewRoutineDesc (codePtr, procInfo, 0);
  448.     Sys.done := 0;
  449.     Sys.Assign ("CallUniversalProc", SYSTEM.ADR (open)); ASSERT(open # NIL);
  450.     IF Sys.done # 0 THEN dnr := NIL; RETURN -1 END;
  451.     res := open (dnr, procInfo, OPENRESOLVER, SYSTEM.VAL (StrPtr, SYSTEM.ADR (filename)));
  452.     IF res # noErr THEN
  453.         HUnlock (codeHndl);
  454.         DisposeHandle (codeHndl);
  455.         dnr := NIL;
  456.     END;
  457.     RETURN res;
  458. END OpenResolver;
  459. PROCEDURE CloseResolver (): INTEGER;
  460.     res: INTEGER;
  461.     close: CloseResProc;
  462.     dnr1: DNRProc;
  463.     codePtr, procInfo: LONGINT;
  464. BEGIN
  465.     IF dnr = NIL THEN RETURN notOpenErr END;
  466.     SYSTEM.GET (codeHndl, codePtr);
  467.     procInfo := 0A1H;
  468.     dnr1 := Sys.NewRoutineDesc (codePtr, procInfo, 0);
  469.     Sys.done := 0;
  470.     Sys.Assign ("CallUniversalProc", SYSTEM.ADR (close)); ASSERT(close # NIL);
  471.     IF Sys.done # 0 THEN RETURN -1 END;
  472.     res := close (dnr1, procInfo, CLOSERESOLVER);
  473.     HUnlock (codeHndl);
  474.     DisposeHandle (codeHndl);
  475.     dnr := NIL;
  476.     RETURN res;
  477. END CloseResolver;
  478. PROCEDURE StrToAddr (hostname: ARRAY OF CHAR; rtnStruct: hostInfoPtr; resultproc: LONGINT; userDataPtr: StrPtr): INTEGER;
  479.     res: INTEGER;
  480.     strtoaddr: StrToAddrProc;
  481.     dnr2: DNRProc;
  482.     codePtr, procInfo: LONGINT;
  483. BEGIN
  484.     IF dnr = NIL THEN RETURN notOpenErr END;
  485.     SYSTEM.GET (codeHndl, codePtr);
  486.     procInfo := 0FFB1H;
  487.     dnr2 := Sys.NewRoutineDesc (codePtr, procInfo, 0);
  488.     Sys.done := 0;
  489.     Sys.Assign ("CallUniversalProc", SYSTEM.ADR (strtoaddr)); ASSERT(strtoaddr # NIL);
  490.     IF Sys.done # 0 THEN RETURN -1 END;
  491.     res := strtoaddr (dnr2, procInfo, STRTOADDR, SYSTEM.VAL (StrPtr, SYSTEM.ADR (hostname)), rtnStruct, resultproc, userDataPtr);
  492.     RETURN res;
  493. END StrToAddr;
  494. (* gResProc is StrToAddrResultProc in m68k code
  495. PROCEDURE StrToAddrResultProc (rtnStruct: hostInfoPtr; userDataPtr: StrPtr);
  496. BEGIN
  497.     gStrToAddrDone := 1;
  498. END StrToAddrResultProc;
  499. PROCEDURE AddrToNameCall (adr: LONGINT; rtnStruct: hostInfoPtr; resultproc: LONGINT; userDataPtr: StrPtr): INTEGER;
  500.     res: INTEGER;
  501.     addrtoname: AddrToNameProc;
  502.     dnr3: DNRProc;
  503.     codePtr, procInfo: LONGINT;
  504. BEGIN
  505.     IF dnr = NIL THEN RETURN notOpenErr END;
  506.     SYSTEM.GET (codeHndl, codePtr);
  507.     procInfo := 0FFB1H;
  508.     dnr3 := Sys.NewRoutineDesc (codePtr, procInfo, 0);
  509.     Sys.done := 0;
  510.     Sys.Assign ("CallUniversalProc", SYSTEM.ADR (addrtoname)); ASSERT(addrtoname # NIL);
  511.     IF Sys.done # 0 THEN RETURN -1 END;
  512.     res := addrtoname (dnr3, procInfo, ADDRTONAME, adr, rtnStruct, resultproc, userDataPtr);
  513.     RETURN res;
  514. END AddrToNameCall;
  515. (** ---------- Address Resolving ---------- *)
  516. PROCEDURE HostByName* (hostname: ARRAY OF CHAR; VAR adr: IpAdr; VAR res: INTEGER);
  517.         (** Given a hostname, the IP address of that machine is returned in adr.
  518.             If no host with the given name could be found, NotDone is returned in res. *)
  519. VAR t: LONGINT;
  520. BEGIN
  521.     adr := 0;
  522.     IF ioCRefNum = 0 THEN res := NotDone; RETURN END;    (* Sys.Assign in module body were possibly not successful *)
  523.     IF hostname = shortNameCache THEN
  524.         adr := addrCache; res := Done;
  525.     ELSE
  526.         res := OpenResolver ("");
  527.         IF res # noErr THEN res := NotDone; RETURN END;
  528.         gStrToAddrDone := 0;
  529.         (* gResProc is StrToAddrResultProc in m68k code *)
  530.         gResProc [0] := 4E56H; (* LINK *)
  531.         gResProc [1] := 0;
  532.         gResProc [2] := 33FCH; (* MOVE.W #1, gStrToAddrDone *)
  533.         gResProc [3] := 1;
  534.         gResProc [4] := SHORT (SYSTEM.ADR (gStrToAddrDone) DIV (256*256));
  535.         gResProc [5] := SHORT (SYSTEM.ADR (gStrToAddrDone) MOD (256*256));
  536.         gResProc [6] := 4E5EH; (* UNLK A6 *)
  537.         gResProc [7] := 4E74H; (* RTD #8 *)
  538.         gResProc [8] := 8;
  539.         t := O.Time ();
  540.         res := StrToAddr (hostname, gRtnStruct, SYSTEM.ADR (gResProc), NIL);
  541.         IF res = cacheFault THEN
  542.             WHILE (gStrToAddrDone = 0) (*& (O.Time () - t < DNRTimeout * Sec)*) DO END; (* wait until timeout *)
  543.             IF gStrToAddrDone = 0 THEN res := Timeout; RETURN END; (* Timeout *)
  544.         ELSIF res # noErr THEN
  545.             res := NotDone; RETURN
  546.         END;
  547.         res := CloseResolver ();
  548.         IF gRtnStruct.rtnCode = noErr THEN
  549.             COPY(hostname, shortNameCache); COPY(gRtnStruct.cname, longNameCache); addrCache := gRtnStruct.addr[0];
  550.             adr := addrCache; res := Done;
  551.         ELSE res := NotDone;
  552.         END;
  553.     END;
  554. END HostByName;
  555. PROCEDURE AddrToName* (adr: IpAdr; VAR name: ARRAY OF CHAR; VAR res: INTEGER);
  556.         (** Mac only: converts an IP address to a hostname. *)
  557. VAR t: LONGINT;
  558. BEGIN
  559.     name[0] := 0X;
  560.     IF ioCRefNum = 0 THEN res := NotDone; RETURN END;    (* Sys.Assign in module body were possibly not successful *)
  561.     IF adr = addrCache THEN
  562.         COPY(longNameCache, name); res := Done;
  563.     ELSE
  564.         res := OpenResolver ("");
  565.         IF res # noErr THEN res := NotDone; RETURN END;
  566.         gStrToAddrDone := 0;
  567.         (* gResProc is StrToAddrResultProc in m68k code *)
  568.         gResProc [0] := 4E56H; (* LINK *)
  569.         gResProc [1] := 0;
  570.         gResProc [2] := 33FCH; (* MOVE.W #1, gStrToAddrDone *)
  571.         gResProc [3] := 1;
  572.         gResProc [4] := SHORT (SYSTEM.ADR (gStrToAddrDone) DIV (256*256));
  573.         gResProc [5] := SHORT (SYSTEM.ADR (gStrToAddrDone) MOD (256*256));
  574.         gResProc [6] := 4E5EH; (* UNLK A6 *)
  575.         gResProc [7] := 4E74H; (* RTD #8 *)
  576.         gResProc [8] := 8;
  577.         t := O.Time ();
  578.         res := AddrToNameCall (adr, gRtnStruct, SYSTEM.ADR (gResProc), NIL);
  579.         IF res = cacheFault THEN
  580.             WHILE (gStrToAddrDone = 0) (*& (O.Time () - t < DNRTimeout * Sec)*) DO END; (* wait until timeout *)
  581.             IF gStrToAddrDone = 0 THEN res := Timeout; RETURN END; (* Timeout *)
  582.         ELSIF res # noErr THEN
  583.             res := NotDone; RETURN
  584.         END;
  585.         res := CloseResolver ();
  586.         IF gRtnStruct.rtnCode = noErr THEN
  587.             COPY(gRtnStruct.cname, longNameCache); COPY(longNameCache, shortNameCache); addrCache := adr;
  588.             COPY(longNameCache, name); res := Done;
  589.         ELSE res := NotDone;
  590.         END;
  591.     END;
  592. END AddrToName;
  593. PROCEDURE HostByNumber* (number: ARRAY OF CHAR; VAR adr: IpAdr; VAR res: INTEGER);
  594.         (** Converts an IP-number-string in dot-notation into an IP address. *)
  595. VAR num, i, j: INTEGER; mask: LONGINT;
  596. BEGIN
  597.     adr := 0; i := 0; j := 0;
  598.     LOOP
  599.         num := 0;
  600.         WHILE (number[i] >= "0") & (number[i] <= "9") DO num := num * 10 + ORD (number[i]) - ORD("0"); INC (i) END;
  601.         adr := ASH (adr, 8) + num;
  602.         INC (j); IF j = 4 THEN EXIT END;
  603.         IF number[i] = "." THEN INC (i) ELSE EXIT END;
  604.     END;
  605.     res := Done;
  606. (* T.WriteString(W, "HostByNumber: ");
  607.     FOR i := -3 TO 0 DO T.WriteInt (W, ASH (adr, 8 * i) MOD 256, 0); IF i < 0 THEN T.Write (W, ".") END END;
  608.     T.WriteLn (W); T.Append (O.Log, W.buf);
  609. END HostByNumber;
  610. PROCEDURE AddrToNumber* (adr: IpAdr; VAR number: ARRAY OF CHAR; VAR res: INTEGER);
  611.         (** Mac only: converts an IP address into an IP-number-string in dot-notation. *)
  612. VAR i, j: INTEGER;
  613.     PROCEDURE WriteShortCard (l: LONGINT);
  614.     VAR k: INTEGER; a: ARRAY 3 OF CHAR;
  615.     BEGIN
  616.         k := 0; REPEAT a[k] := CHR (l MOD 10 + ORD ("0")); l := l DIV 10; INC (k) UNTIL l = 0;
  617.         REPEAT DEC (k); number[j] := a[k]; INC (j) UNTIL k = 0;
  618.     END WriteShortCard;
  619. BEGIN
  620.     j := 0;
  621.     FOR i := -3 TO 0 DO WriteShortCard (ASH (adr, 8 * i) MOD 256); IF i < 0 THEN number[j] := "."; INC(j) END END;
  622.     number[j] := 0X; res := Done;
  623. END AddrToNumber;
  624. PROCEDURE ^ GetMyIPAddr (VAR ourAddr, ourMask: LONGINT);
  625. PROCEDURE GetHostAddress*(VAR adr: IpAdr; VAR res: INTEGER);
  626.         (** Mac only: Returns the local IP address *)
  627. VAR mask: LONGINT;
  628. BEGIN
  629.     GetMyIPAddr(adr, mask);
  630.     IF adr # 0 THEN res := Done ELSE res := NotDone END;
  631. END GetHostAddress;
  632. PROCEDURE GetHostName*(VAR s: ARRAY OF CHAR; VAR res: INTEGER);
  633.         (** Returns the local hostname *) (* redundant, required by a version of TCP.Def *)
  634. VAR adr: IpAdr;
  635. BEGIN
  636.     GetHostAddress(adr, res);
  637.     IF res = Done THEN AddrToName(adr, s, res) ELSE s[0] := 0X; res := NotDone END;
  638. END GetHostName;
  639. (* ---------- TCP Implementation Procs ---------- *)
  640. PROCEDURE WriteRes (csCode: INTEGER; res: INTEGER);
  641. BEGIN
  642.     IF res # noErr THEN
  643.         CASE csCode OF
  644.             csPBOpen: T.WriteString(W, " PBOpen: ");
  645.             |csIPctlGetAddr: T.WriteString(W," IPctlGetAddr: ");
  646.             |csUDPMaxMTUSize: T.WriteString(W," UDPMaxMTUSize: ");
  647.             |csTCPCreate: T.WriteString(W," TCPCreate: ");
  648.             |csTCPPassiveOpen: T.WriteString(W," TCPPassiveOpen: ");
  649.             |csTCPActiveOpen: T.WriteString(W," TCPActiveOpen: ");
  650.             |csTCPSend: T.WriteString(W," TCPSend: ");
  651.             |csTCPNoCopyRcv: T.WriteString(W," TCPNoCopyRcv: ");
  652.             |csTCPBfrReturn: T.WriteString(W," TCPBfrReturn: ");
  653.             |csTCPRcv: T.WriteString(W," TCPRcv: ");
  654.             |csTCPClose: T.WriteString(W," TCPClose: ");
  655.             |csTCPAbort: T.WriteString(W," TCPAbort: ");
  656.             |csTCPStatus: T.WriteString(W," TCPStatus: ");
  657.             |csTCPRelease: T.WriteString(W," TCPRelease: ");
  658.             |csTCPGlobalInfo: T.WriteString(W," TCPGlobalInfo: ");
  659.         ELSE
  660.             T.WriteString(W," TCP: Unknown command: "); T.WriteInt (W, csCode, 0); T.WriteString (W, ": ");
  661.         END;
  662.         CASE res OF (* MacTCP return Codes in the range -23000 through -23048; for further information see MacTCP manual *)
  663.             ipBadLapErr: T.WriteString(W,"Unable to initialize the local network handler ");
  664.             |ipBadAddr: T.WriteString(W,"Error in getting address from server or address is already in use ");
  665.             |connectionClosing: T.WriteString(W,"A TCPClose command was already issued ");
  666.             |invalidLength: T.WriteString(W,"The total amount of data described by the WDS was either 0 or > 65535 bytes ");
  667.             |connectionExists: T.WriteString(W,"This TCP stream already has an open connection ");
  668.             |connectionDoesntExist: T.WriteString(W,"This TCP stream has no open connection ");
  669.             |insufficientResources: T.WriteString(W,"64 TCP streams are already open ");
  670.             |invalidStreamPtr: T.WriteString(W,"The specified TCP stream is not open ");
  671.             |streamAlreadyOpen: T.WriteString(W,"An open stream is already using this receive buffer area ");
  672.             |connectionTerminated: T.WriteString(W,"The TCP connection was broken ");
  673.             |invalidBufPtr: T.WriteString(W,"The receive buffer area pointer is 0 ");
  674.             |invalidRWDS: T.WriteString(W,"The RDS or WDS pointer was not correct ");
  675.             |openFailed: T.WriteString(W,"The connection came halfway up and then failed ");
  676.             |commandTimeout: T.WriteString(W,"Command timeout ");
  677.             |duplicateSocket:
  678.                 T.WriteString(W,"A TCP connection already exists between the local and the remote addresses and ports ");
  679.         ELSE (* ELSE to avoid case range too large *)
  680.             CASE res OF
  681.                 inProgress: T.WriteString(W,"I/O in progress ");
  682.                 |controlErr: T.WriteString(W,"Driver can't respond to this control request ");
  683.                 |statusErr: T.WriteString(W,"Driver can't respond to this status request ");
  684.                 |readErr: T.WriteString(W,"Driver can't respond to this read request ");
  685.                 |writeErr: T.WriteString(W,"Driver can't respond to this write request ");
  686.                 |badUnitErr: T.WriteString(W,"Reference number out of range or not in use ");
  687.                 |unitEmptyErr: T.WriteString(W,"Bad reference number ");
  688.                 |openErr: T.WriteString(W,"Driver can't perform requested reading or writing ");
  689.                 |notOpenErr: T.WriteString(W,"Driver isn't open ");
  690.                 |abortErr: T.WriteString(W,"Request aborted by KillIO ");
  691.                 |ioErr: T.WriteString(W,"Data doesn't match in read-verify mode ");
  692.             ELSE
  693.                 T.WriteString(W,"Unknown error: "); T.WriteInt(W, res, 0);
  694.             END;
  695.         END;
  696.         T.WriteLn(W); T.Append(O.Log, W.buf);
  697.     END;
  698. END WriteRes;
  699. PROCEDURE WriteStatus (statusRes: INTEGER; status: SHORTINT; unread: INTEGER; sendable: LONGINT);
  700. BEGIN
  701.     IF statusRes = noErr THEN
  702.         T.WriteString(W,"  TCP connection state: ");
  703.         CASE status OF
  704.             Closed: T.WriteString(W,"Closed ");
  705.             |Listening: T.WriteString(W,"Listen ");
  706.             |SYNrcv: T.WriteString(W,"SYNrcv ");
  707.             |SYNsnd: T.WriteString(W,"SYNsnd ");
  708.             |Established: T.WriteString(W,"Established ");
  709.             |FINWait1: T.WriteString(W,"FINWait1 ");
  710.             |FINWait2: T.WriteString(W,"FINWait2 ");
  711.             |CloseWait: T.WriteString(W,"CloseWait ");
  712.             |Closing: T.WriteString(W,"Closing ");
  713.             |LastAck: T.WriteString(W,"LastAck ");
  714.             |TimeWait: T.WriteString(W,"TimeWait ");
  715.         ELSE
  716.             T.WriteString (W,"Unknown state: "); T.WriteInt (W, status, 0);
  717.         END;
  718.         T.WriteString (W,"  unread: "); T.WriteInt (W, unread, 0);
  719.         T.WriteString (W,"  sendable: "); T.WriteInt (W, sendable, 0);
  720.         T.WriteLn(W); T.Append(O.Log, W.buf);
  721.     ELSE
  722.         WriteRes (csTCPStatus, statusRes);
  723.     END;
  724. END WriteStatus;
  725. PROCEDURE StrToStr255 (VAR str255: Str255; str: ARRAY OF CHAR);
  726. VAR i: INTEGER; ch: CHAR;
  727. BEGIN
  728.     i := 0; REPEAT ch := str[i]; INC(i); str255[i] := ch UNTIL (ch = 0X) OR (i = 256);
  729.     str255[0] := CHR(i-1)
  730. END StrToStr255;
  731. PROCEDURE FreeConnTabEntry(): INTEGER;
  732. VAR i: INTEGER;
  733. BEGIN
  734.     i := 0; WHILE i < MaxStreams DO IF connTab[i] = 0 THEN RETURN i END; INC(i) END;
  735.     RETURN -1;
  736. END FreeConnTabEntry;
  737. PROCEDURE FreeListTabEntry(): INTEGER;
  738. VAR i: INTEGER;
  739. BEGIN
  740.     i := 0; WHILE i < MaxStreams DO IF listTab[i] = 0 THEN RETURN i END; INC(i) END;
  741.     RETURN -1;
  742. END FreeListTabEntry;
  743. PROCEDURE ThisConn(id: LONGINT): INTEGER;
  744. (* returns the index into connTab where connTab[index].id = id, or -1 *)
  745. VAR i: INTEGER; C: Connection;
  746. BEGIN
  747.     i := 0;
  748.     WHILE i < MaxStreams DO
  749.         IF connTab[i] # 0 THEN
  750.             C := SYSTEM.VAL(Connection, connTab[i]);
  751.             IF C.id = id THEN RETURN i END;
  752.         END;
  753.         INC(i);
  754.     END;
  755.     RETURN -1;
  756. END ThisConn;
  757. PROCEDURE ThisList(id: LONGINT): INTEGER;
  758. (* returns the index into listTab where listTab[index].id = id, or -1 *)
  759. VAR i: INTEGER; L: Listener;
  760. BEGIN
  761.     i := 0;
  762.     WHILE i < MaxStreams DO
  763.         IF listTab[i] # 0 THEN
  764.             L := SYSTEM.VAL(Listener, listTab[i]);
  765.             IF L.id = id THEN RETURN i END;
  766.         END;
  767.         INC(i);
  768.     END;
  769.     RETURN -1;
  770. END ThisList;
  771. (* ---------- MacTCP ---------- *)
  772. PROCEDURE ControlDev (PBControlPb: PBControlPtr; timeout: LONGINT): INTEGER;
  773. (* device control call; synchronous if timeout = 0, else asynchronous: wait for result if timeout (in seconds) > 0 *)
  774. VAR res: INTEGER; t: LONGINT;
  775. BEGIN
  776.     IF timeout = 0 THEN (* means infinite timeout: use synchronous call *)
  777.         res := PBControlSync (PBControlPb);
  778.     ELSE
  779.         t := O.Time ();
  780.         res := PBControlAsync (PBControlPb);
  781.         IF (timeout > 0) & (res = noErr) THEN
  782.             WHILE (PBControlPb.ioResult = inProgress) & (O.Time () - t < timeout * Sec) DO END;
  783.                 (* wait timeout seconds *)
  784.             res := PBControlPb.ioResult;
  785.         END;
  786.     END;
  787. (* IF res # noErr THEN WriteRes (PBControlPb.csCode, res) END;*)
  788.     RETURN res;
  789. END ControlDev;
  790. PROCEDURE UDPGetMTUSize (remAdr: LONGINT): INTEGER;
  791. (* Gets the max MTU size from the net *)
  792. VAR UdpMtuPb: UdpMtuPBPtr; res: INTEGER;
  793. BEGIN
  794.     IF ioCRefNum # 0 THEN
  795.         NEW (UdpMtuPb);
  796.         UdpMtuPb.ioCRefNum := ioCRefNum;
  797.         UdpMtuPb.csCode := csUDPMaxMTUSize;
  798.         UdpMtuPb.remAdr := remAdr;
  799.         UdpMtuPb.usrDataPtr := 0;
  800.         res := ControlDev (UdpMtuPb, SyncTimeout);
  801.         IF res = noErr THEN RETURN UdpMtuPb.maxDataSize ELSE RETURN 0 END;
  802.     ELSE RETURN 0
  803.     END;
  804. END UDPGetMTUSize;
  805. PROCEDURE GetMyIPAddr (VAR ourAddr, ourMask: LONGINT);
  806. (* Returns subnet mask & IPaddress *)
  807. VAR GetAddrPb: GetAddrPBPtr; res: INTEGER;
  808. BEGIN
  809.     IF ioCRefNum # 0 THEN
  810.         NEW (GetAddrPb);
  811.         GetAddrPb.ioCRefNum := ioCRefNum;
  812.         GetAddrPb.csCode := csIPctlGetAddr;
  813.         res := ControlDev (GetAddrPb, SyncTimeout);
  814.         IF res = noErr THEN ourAddr := GetAddrPb.ourAddr; ourMask := GetAddrPb.ourMask;
  815.         ELSE ourAddr := 0; ourMask := 0;
  816.         END;
  817.     ELSE ourAddr := 0; ourMask := 0;
  818.     END;
  819. END GetMyIPAddr; 
  820. PROCEDURE InitDriver;
  821. VAR res: INTEGER; PBOpenBlock: PBOpenPtr; ourAddr, ourMask: LONGINT; name255: Str255;
  822. BEGIN
  823.     StrToStr255 (name255, DeviceID);
  824.     NEW (PBOpenBlock);
  825.     PBOpenBlock.ioCompletion := 0;
  826.     PBOpenBlock.ioNamePtr := SYSTEM.VAL (LONGINT, SYSTEM.ADR (name255));
  827.     PBOpenBlock.ioPermissions := fsCurPerm;
  828.     res := PBOpenSync (PBOpenBlock);
  829. (* WriteRes (csPBOpen, res);*)
  830.     IF res = noErr THEN
  831.         ioCRefNum := PBOpenBlock.ioCRefNum;
  832.     ELSE
  833.         ioCRefNum := 0;
  834.         T.WriteString (W, "Could not open MacTCP driver"); T.WriteLn (W); T.Append (O.Log, W.buf);
  835.     END;
  836.     GetMyIPAddr (ourAddr, ourMask);
  837.     maxMTUsize := UDPGetMTUSize (ourAddr);
  838.     recBufferSize := 4 * maxMTUsize + 1024;
  839.     IF recBufferSize < RecBufferSize THEN recBufferSize := RecBufferSize END;
  840. END InitDriver;
  841. PROCEDURE InitStreamPB (pbs: Stream);
  842. (* assuming CreatePb is already initialized, initializes stream's other 5 PBs and keeps references to them in pbs to prevent
  843.     gc from freeing them *)
  844. VAR OpenPb: OpenPBPtr; ClosePb: ClosePBPtr; StatusPb: StatusPBPtr; SendPb: SendPBPtr; RcvPb: RcvPBPtr;
  845. BEGIN
  846.     NEW (OpenPb);
  847.     OpenPb.ioCRefNum := ioCRefNum;
  848.     OpenPb.streamPtr := pbs.CreatePb.streamPtr;
  849.     OpenPb.ULPtoVal := ULPTimeout;
  850.     OpenPb.ULPtoAct := 1;
  851.     OpenPb.validity := 0CH;
  852.     OpenPb.serviceType := 0;
  853.     OpenPb.precedence := 0;
  854.     OpenPb.dontfrag := 0;
  855.     OpenPb.livetime := 60;
  856.     OpenPb.security := 0;
  857.     OpenPb.IPoptcount := 0;
  858.     OpenPb.usrDataPtr := 0;
  859.     pbs.OpenPb := OpenPb;
  860.     NEW (ClosePb);
  861.     ClosePb.ioCRefNum := ioCRefNum;
  862.     ClosePb.csCode := csTCPClose;
  863.     ClosePb.streamPtr := pbs.CreatePb.streamPtr;
  864.     ClosePb.ULPtoVal := ULPTimeout;
  865.     ClosePb.ULPtoAct := 1;
  866.     ClosePb.validity := 0CH;
  867.     ClosePb.usrDataPtr := 0;
  868.     pbs.ClosePb := ClosePb;
  869.     NEW (StatusPb);
  870.     StatusPb.ioCRefNum := ioCRefNum;
  871.     StatusPb.csCode := csTCPStatus;
  872.     StatusPb.streamPtr := pbs.CreatePb.streamPtr;    
  873.     StatusPb.usrDataPtr := 0;
  874.     StatusPb.ioResult := noErr; (* # inProgress *)
  875.     pbs.StatusPb := StatusPb;
  876.     NEW (SendPb);
  877.     NEW (SendPb.wdsPtr);
  878.     SendPb.wdsPtr.zero := 0;
  879.     SendPb.ioCRefNum := ioCRefNum;
  880.     SendPb.csCode := csTCPSend;
  881.     SendPb.streamPtr := pbs.CreatePb.streamPtr;
  882.     SendPb.ULPtoVal := ULPTimeout;
  883.     SendPb.ULPtoAct := 1;
  884.     SendPb.validity := 0CH;
  885.     SendPb.pushflag := 1;
  886.     SendPb.urgentflag := 0;
  887.     SendPb.usrDataPtr := 0;
  888.     SendPb.ioResult := noErr; (* # inProgress *)
  889.     pbs.SendPb := SendPb;
  890.     NEW (RcvPb);
  891.     RcvPb.ioCRefNum := ioCRefNum;
  892.     RcvPb.csCode := csTCPRcv;
  893.     RcvPb.streamPtr := pbs.CreatePb.streamPtr;
  894.     RcvPb.cmdto := CmdTimeout;
  895.     RcvPb.usrDataPtr := 0;
  896.     RcvPb.ioResult := noErr; (* # inProgress *)
  897.     pbs.RcvPb := RcvPb;
  898. END InitStreamPB;
  899. PROCEDURE ^ Finalizer;
  900. PROCEDURE ^ ReleaseAll;
  901. PROCEDURE TCPCreate(C: Connection; L: Listener; len, id: LONGINT; i: INTEGER);
  902. (* MacTCP create command, gets stream; either C or L, the other is NIL *)
  903. VAR pbs: Stream; CreatePb: CreatePBPtr; mod: Modules.Module; res: INTEGER;
  904. BEGIN
  905.     NEW(CreatePb);
  906.     CreatePb.bufferPtr := NewPtr(len); (* or SYSTEM.NEW (CreatePb.bufferPtr, len); then no DisposePtr *)
  907. ASSERT (CreatePb.bufferPtr # 0);
  908.     IF CreatePb.bufferPtr # 0 THEN
  909.         CreatePb.ioCRefNum := ioCRefNum;
  910.         CreatePb.csCode := csTCPCreate;
  911.         CreatePb.bufferLen := len;
  912.         CreatePb.ASRPtr := 0;
  913.         CreatePb.usrDataPtr := 0;
  914.         res := ControlDev(CreatePb, SyncTimeout); (* synchronous *)
  915.         IF res # noErr THEN DisposePtr(CreatePb.bufferPtr);
  916.         ELSE
  917.             NEW(pbs);
  918.             pbs.res := res;
  919.             pbs.CreatePb := CreatePb;
  920.             InitStreamPB(pbs); (* create and init the other 5 PBs *)
  921.             IF C # NIL THEN
  922.                 connTab[i] := SYSTEM.VAL(LONGINT, C); INC(connCount); C.pbs := pbs; C.id := id;
  923.             ELSE
  924.                 listTab[i] := SYSTEM.VAL(LONGINT, L); INC(listCount); L.pbs := pbs; L.id := id;
  925.             END;
  926.             IF ((connCount = 1) & (listCount = 0)) OR ((connCount = 0) & (listCount = 1)) THEN
  927.                 mod := Modules.modules;
  928.                 WHILE (mod # NIL) & (mod.name # "TCP") DO mod := mod.link END;
  929.                 IF mod # NIL THEN SYSTEM.PUT (SYSTEM.ADR (mod.refcnt), mod.refcnt + 1) END;
  930.                 (* increment readonly refcnt to prevent module freeing; freeing will be allowed again after all streams have been
  931.                     released with TCPRelease, either normally or by finalization (if possible) by using System.Collect *)
  932.                 Kernel.gcQ.Add(Finalizer); (* gcQ: no NEW in or via Finalizer! *)
  933.                 Kernel.quitQ.Add(ReleaseAll);
  934.             END;
  935.         END;
  936.     END;
  937. END TCPCreate;
  938. PROCEDURE TCPRelease(C: Connection; L: Listener);
  939. (* MacTCP release command, releases stream; either C or L, the other is NIL *)
  940. VAR i: LONGINT; mod: Modules.Module; pbs: Stream;
  941. BEGIN
  942.     (* CreatePb.ioResult (sync) = noErr # inProgress; uses same data (structure) as TCPCreate *)
  943.     IF C # NIL THEN i := ThisConn(C.id); pbs := C.pbs ELSE i := ThisList(L.id); pbs := L.pbs END;
  944.     pbs.CreatePb.csCode := csTCPRelease;
  945.     pbs.res := ControlDev(pbs.CreatePb, SyncTimeout); (* synchronous *)
  946.     DisposePtr(pbs.CreatePb.bufferPtr);
  947.     IF i >= 0 THEN
  948.         IF C # NIL THEN connTab[i] := 0; DEC(connCount) ELSE listTab[i] := 0; DEC(listCount) END;
  949.         IF (connCount = 0) & (listCount = 0) THEN
  950.             Kernel.gcQ.Remove(Finalizer);
  951.             Kernel.quitQ.Remove(ReleaseAll);
  952.             mod := Modules.modules;
  953.             WHILE (mod # NIL) & (mod.name # "TCP") DO mod := mod.link END;
  954.             IF mod # NIL THEN SYSTEM.PUT (SYSTEM.ADR (mod.refcnt), mod.refcnt - 1) END;
  955.             (* decrement readonly refcnt (> 0) to allow module freeing *)
  956.         END;
  957.     END;
  958. END TCPRelease;
  959. PROCEDURE* Finalizer;
  960.     (* called between mark and scan phases of garbage collector to release unreferenced streams; so this module may not be freed;
  961.     do not use NEW and TCPRelease/ControlDev/Oberon.Time may not use NEW (no output to screen, no WriteRes) *)
  962. VAR i: INTEGER; tag: LONGINT;
  963. BEGIN
  964.     FOR i := 0 TO MaxStreams - 1 DO
  965.         IF connTab[i] # 0 THEN (* unreleased stream; check if also unreferenced: *)
  966.             SYSTEM.GET(connTab[i] - 4, tag);
  967.             (* connTab[i] is pointer to (is address of) ConnectionDesc; record tag is 4 bytes before *)
  968.             IF tag MOD 2 = 0 THEN (* is not marked as referenced record; will be freed during scan phase *)
  969.                 TCPRelease(SYSTEM.VAL(Connection, connTab[i]), NIL); (* release stream *)
  970.             END;
  971.         END;
  972.         IF listTab[i] # 0 THEN (* unreleased stream; check if also unreferenced: *)
  973.             SYSTEM.GET(listTab[i] - 4, tag);
  974.             (* listTab[i] is pointer to (is address of) ListenerDesc; record tag is 4 bytes before *)
  975.             IF tag MOD 2 = 0 THEN (* is not marked as referenced record; will be freed during scan phase *)
  976.                 TCPRelease(NIL, SYSTEM.VAL(Listener, listTab[i])); (* release stream *)
  977.             END;
  978.         END;
  979.     END;
  980. END Finalizer;
  981. PROCEDURE* ReleaseAll (* * *);    (* called by quitQ to release all streams; exported only for testing *)
  982. VAR i, s: INTEGER;
  983. BEGIN
  984.     s := 0;
  985.     FOR i := 0 TO MaxStreams - 1 DO
  986.         IF connTab[i] # 0 THEN TCPRelease(SYSTEM.VAL(Connection, connTab[i]), NIL); INC(s) END;
  987.         IF listTab[i] # 0 THEN TCPRelease(NIL, SYSTEM.VAL(Listener, listTab[i])); INC(s) END;
  988.     END;
  989.     IF s > 0 THEN
  990.         T.WriteString(W, "TCP: Released "); T.WriteInt(W, s, 0); T.WriteString(W, " stream(s)");
  991.         T.WriteLn(W); T.Append(O.Log, W.buf);
  992.     END;
  993. END ReleaseAll;
  994. PROCEDURE TCPStatus (S: Stream);
  995. (* MacTCP status command; returns the connection state (and gets other statistics) from MacTCP; see MacTCP manual *)
  996. BEGIN
  997.     (* StatusPb initialized in InitStreamPB *)
  998.     IF S.StatusPb.ioResult # inProgress THEN (* uses stream's StatusPb; check if PB is still in use *)
  999.         S.res := ControlDev (S.StatusPb, StatusTimeout);
  1000.     (* WriteStatus (S.res, S.StatusPb.connState, S.StatusPb.unreadData,
  1001.             S.StatusPb.sndWindow - ABS (S.StatusPb.sndNext - S.StatusPb.sndUnacknowledged));*)
  1002.     END;
  1003. END TCPStatus;
  1004. PROCEDURE TCPOpen (S: Stream; remAdr: LONGINT; lport, rport: INTEGER; timeout: LONGINT);
  1005. (* MacTCP open command, opens a connection, active or passive *)
  1006. VAR OpenPb: OpenPBPtr;
  1007. BEGIN
  1008.     (* OpenPb initialized in InitStreamPB *)
  1009.     OpenPb := S.OpenPb;
  1010.     IF timeout >= 0 THEN (* active; ControlDev will wait for result; asynchronous if timeout > 0 *)
  1011.         OpenPb.csCode := csTCPActiveOpen;
  1012.         OpenPb.cmdto := 0;    (* active: not used by TCP *)
  1013.         OpenPb.remAdr := remAdr;    (* cannot be 0! *)    
  1014.         OpenPb.remPort := rport;        (* cannot be 0! *)
  1015.         OpenPb.localPort := lport;        (* may be 0 *)
  1016.     ELSE (* passive, asynchronous; ControlDev will return without waiting *)
  1017.         OpenPb.ioCompletion := 0;
  1018.         OpenPb.csCode := csTCPPassiveOpen;
  1019.         OpenPb.cmdto := 0;    (* passive: 0 = infinity *)
  1020.         OpenPb.remAdr := 0;        (* must be 0! - proc. param. remAdr not used *)
  1021.         OpenPb.remPort := 0;        (* must be 0! - proc. param. rport not used *)
  1022.         OpenPb.localPort := lport;    (* cannot be 0! *)
  1023.     END;
  1024.     S.res := ControlDev (OpenPb, timeout);
  1025. END TCPOpen;
  1026. PROCEDURE TCPClose (S: Stream);
  1027. (* MacTCP close command, closes a connection *)
  1028. BEGIN
  1029.     (* ClosePb initialized in InitStreamPB *)
  1030.     S.res := ControlDev (S.ClosePb, CloseTimeout);
  1031. END TCPClose;
  1032. PROCEDURE TCPSend (C: Connection; buf: BufferPtr; len: INTEGER);
  1033. (* MacTCP send command, sends data stored in the buf (via wds structure) *)
  1034. VAR SendPb: SendPBPtr;
  1035. BEGIN
  1036.     (* SendPb initialized in InitStreamPB *)
  1037.     SendPb := C.pbs.SendPb;
  1038.     IF SendPb.ioResult # inProgress THEN
  1039.         SendPb.wdsPtr.bufLen := len;
  1040.         SendPb.wdsPtr.buf := buf;
  1041.         C.pbs.res := ControlDev (SendPb, SendRcvTimeout);
  1042.         IF C.pbs.res = noErr THEN res := Done ELSE res := NotDone END;
  1043.     ELSE res := NotDone;
  1044.     END;
  1045. END TCPSend;
  1046. PROCEDURE TCPRcv (C: Connection; rcvBufPtr: LONGINT; VAR rcvBufLen: INTEGER);
  1047. (* MacTCP receive command, receives data and stores (copies) it into the rcvBuf *)
  1048. VAR RcvPb: RcvPBPtr;
  1049. BEGIN
  1050.     (* RcvPb initialized in InitStreamPB *)
  1051.     RcvPb := C.pbs.RcvPb;
  1052.     IF RcvPb.ioResult # inProgress THEN
  1053.         RcvPb.rcvBufPtr := rcvBufPtr;
  1054.         RcvPb.rcvBufLen := rcvBufLen;
  1055.         C.pbs.res := ControlDev (RcvPb, SendRcvTimeout);
  1056.         IF C.pbs.res = noErr THEN rcvBufLen := RcvPb.rcvBufLen; res := Done ELSE rcvBufLen := 0; res := NotDone END;
  1057.     ELSE rcvBufLen := 0; res := NotDone;
  1058.     END;
  1059. END TCPRcv;
  1060. PROCEDURE TCPAbort (S: Stream);
  1061. (* MacTCP abort command, aborts a connection; data may be lost; not used *)
  1062. VAR AbortPb: AbortPBPtr;
  1063. BEGIN
  1064.     NEW (AbortPb);
  1065.     AbortPb.ioCRefNum := ioCRefNum;
  1066.     AbortPb.csCode := csTCPAbort;
  1067.     AbortPb.streamPtr := S.CreatePb.streamPtr;
  1068.     AbortPb.usrDataPtr := 0;
  1069.     S.res := ControlDev (AbortPb, CloseTimeout);
  1070. END TCPAbort;
  1071. PROCEDURE TCPNoCopyRcv (C: Connection; rdsPtr: RWDSPtr; VAR RcvPb: RcvPBPtr);
  1072. (* MacTCP receive command, receives data and stores it into the rds structure; not used *)
  1073. BEGIN
  1074.     NEW (RcvPb);
  1075.     RcvPb.ioCRefNum := ioCRefNum;
  1076.     RcvPb.csCode := csTCPNoCopyRcv;
  1077.     RcvPb.streamPtr := C.pbs.CreatePb.streamPtr;
  1078.     RcvPb.cmdto := CmdTimeout;
  1079.     RcvPb.rdsPtr := rdsPtr;
  1080.     RcvPb.numOfEntries := RWdsEntries;
  1081.     RcvPb.usrDataPtr := 0;
  1082.     C.pbs.res := ControlDev (RcvPb, SendRcvTimeout);
  1083. END TCPNoCopyRcv;
  1084. PROCEDURE TCPBfrReturn (C: Connection; rdsPtr: RWDSPtr; RcvPb: RcvPBPtr);
  1085. (* MacTCP bfrreturn command, returns the rds buffer to MacTCP, to be used after processing data received by TCPNoCopyRcv;
  1086.     not used; TCPBfrReturn should use same data structure as TCPNoCopyRcv *)
  1087. BEGIN
  1088.     RcvPb.ioCRefNum := ioCRefNum;
  1089.     RcvPb.csCode := csTCPBfrReturn;
  1090.     RcvPb.streamPtr := C.pbs.CreatePb.streamPtr;
  1091.     RcvPb.rdsPtr := rdsPtr;
  1092.     RcvPb.usrDataPtr := 0;
  1093.     C.pbs.res := ControlDev (RcvPb, SendRcvTimeout);
  1094. END TCPBfrReturn;
  1095. PROCEDURE TCPGlobalInfo (* * *);    (* exported only for testing *)
  1096. VAR GlobalInfoPb: GlobalInfoPBPtr; res: INTEGER;
  1097. BEGIN
  1098.     NEW (GlobalInfoPb);
  1099.     GlobalInfoPb.ioCRefNum := ioCRefNum;
  1100.     GlobalInfoPb.csCode := csTCPGlobalInfo;
  1101.     GlobalInfoPb.usrDataPtr := 0;
  1102.     res := ControlDev (GlobalInfoPb, SyncTimeout);
  1103.     T.WriteString (W, " TCPGlobalInfoPB = "); T.WriteHex (W, SYSTEM.VAL (LONGINT, GlobalInfoPb)); T.Write (W, "H");
  1104.     T.WriteLn (W); T.Append (O.Log, W.buf);
  1105.     IF res = noErr THEN
  1106.         T.WriteString (W, "  TCPparam = "); T.WriteHex (W, GlobalInfoPb.TCPparamPtr); T.Write (W, "H"); T.WriteLn (W);
  1107.         T.WriteString (W, "  TCPstats = "); T.WriteHex (W, GlobalInfoPb.TCPstatsPtr); T.Write (W, "H"); T.WriteLn (W);
  1108.         T.WriteString (W, "  CDBtable = "); T.WriteHex (W, GlobalInfoPb.CDBtablePtr); T.Write (W, "H"); T.WriteLn (W);
  1109.         T.WriteString (W, "  maxTCPconnections = "); T.WriteInt (W, GlobalInfoPb.maxTCPconnections, 0); T.WriteLn (W);
  1110.         T.Append (O.Log, W.buf);
  1111.     END;
  1112. END TCPGlobalInfo;
  1113. (* ---------- Exported Procedures ---------- *)
  1114. (** ---------- Listener ---------- *)
  1115. (** Servers use a listener object to listen on a specific port number. A listener is initialized and started with Listen. The server subsequently uses Requested to determine if a client wishes to connect to the port. If Requested(L) is TRUE, the server accepts the connection request by calling Accept(L, C, res). The server stops a listener object from listening to a port by calling Close(L).
  1116. Ideally, the server would poll Requested in an Oberon.Task and from there would create a new Oberon.Task for every accepted connection which would poll Available and AvailToSend for the connection. *)
  1117. PROCEDURE Listen* (L: Listener; lport: INTEGER; radr: IpAdr; rport: INTEGER; VAR res: INTEGER);
  1118.         (** Listens for incoming connection requests on the local port number lport. Only connection requests
  1119.             from radr and rport will be accepted. radr may be AnyAdr, rport may be AnyPort. lport must not
  1120.             be AnyPort. If the local port number lport is already in use, res is set to LocalPortInUse.
  1121.             Mac: radr and rport are ignored, i.e. any connection request will be accepted. *)
  1122. VAR i: INTEGER;
  1123. BEGIN
  1124.     ASSERT (lport # 0);
  1125.     i := FreeListTabEntry();
  1126.     IF (ioCRefNum = 0) OR (i < 0) THEN res := NotDone; RETURN END;
  1127.     TCPCreate(NIL, L, recBufferSize, O.Time(), i);
  1128.     IF listTab[i] = 0 THEN res := NotDone; RETURN END;
  1129.     TCPOpen(L.pbs, 0, lport, 0, -1); (* passive open, asynchronous *)
  1130.     IF L.pbs.res = noErr THEN
  1131.         L.lport := lport;
  1132.         L.listening := TRUE;
  1133.         res := Done;
  1134.     ELSE
  1135.         IF L.pbs.res = openFailed THEN res := Timeout;
  1136.         ELSIF L.pbs.res = duplicateSocket THEN res := LocalPortInUse;
  1137.         ELSE res := NotDone;
  1138.         END;
  1139.         TCPRelease(NIL, L);
  1140.     END;
  1141. END Listen;
  1142. PROCEDURE Close* (L: Listener);
  1143.         (** Listener L stops listening to its port and releases the port. *)
  1144. BEGIN
  1145.     L.listening := FALSE;
  1146.     TCPStatus(L.pbs);
  1147.     IF L.pbs.res = noErr THEN TCPClose(L.pbs) END;
  1148.     TCPRelease(NIL, L);
  1149. END Close;
  1150. PROCEDURE Requested* (L: Listener): BOOLEAN;
  1151.         (** TRUE if the listener L detected a connection request on its port. *)
  1152. BEGIN
  1153.     IF L.listening THEN                                    (* passive open in progress *)
  1154.         IF L.pbs.OpenPb.ioResult # inProgress THEN    (* passive open has completed *)
  1155.             IF L.pbs.OpenPb.ioResult = noErr THEN        (* ... successfully *)
  1156.                 RETURN TRUE;
  1157.             ELSE                                                (* ... in error *)
  1158.                 (*T.WriteString (W, " Listening: "); T.Append (O.Log, W.buf);*)
  1159.                 (*WriteRes (L.pbs.OpenPb.csCode, L.pbs.OpenPb.ioResult);*)
  1160.                 L.listening := FALSE;
  1161.                 TCPRelease(NIL, L);
  1162.                 RETURN FALSE;
  1163.             END;
  1164.         ELSE    (* passive open still in progress *)
  1165.             RETURN FALSE;
  1166.         END;
  1167.     ELSE    (* no passive open in progress: error *)
  1168.         RETURN FALSE;
  1169.     END;
  1170. END Requested;
  1171. PROCEDURE Accept* (L: Listener; C: Connection; VAR res: INTEGER);
  1172.         (** If Requested(L), Accept opens connection C to the peer making a connection request.
  1173.             Blocks until Requested(L) holds.
  1174.             Mac: does not block, but returns NotDone if Requested(L) does not hold. *)
  1175. VAR i, j: INTEGER;
  1176. BEGIN
  1177.     IF L.listening THEN                                (* passive open in progress *)
  1178.         i := ThisList(L.id); j := FreeConnTabEntry();
  1179.         IF (L.pbs.OpenPb.ioResult = noErr) & (i >= 0) & (j >= 0) THEN        (* passive open has completed successfully *)
  1180.             C.pbs := L.pbs; (* C now uses L's stream *)
  1181.             C.id := O.Time();
  1182.             C.lport := C.pbs.OpenPb.localPort; C.radr := C.pbs.OpenPb.remAdr; C.rport := C.pbs.OpenPb.remPort;
  1183.             connTab[j] := SYSTEM.VAL(LONGINT, C); listTab[i] := 0; INC(connCount); DEC(listCount);
  1184.             (* Accept copies internal data from Listener to Connection after successful completion of passive open (above)
  1185.                 and immediately starts a new passive open overwriting old L (below)
  1186.                 (therefore, maximum number of accepted connections is MaxStreams-1). *)
  1187.             TCPCreate(NIL, L, recBufferSize, L.id, i);
  1188.             IF (listTab[i] = 0) OR (L.pbs.res # noErr) THEN
  1189.                 L.listening := FALSE;
  1190.                 TCPRelease(C, NIL);
  1191.                 res := NotDone;
  1192.                 RETURN
  1193.             END;
  1194.             TCPOpen(L.pbs, 0, L.lport, 0, -1); (* passive open, asynchronous *)
  1195.             IF L.pbs.res = noErr THEN
  1196.                 L.listening := TRUE;
  1197.                 res := Done;
  1198.             ELSE
  1199.                 L.listening := FALSE;
  1200.                 TCPRelease(C, NIL);
  1201.                 TCPRelease(NIL, L);
  1202.                 res := NotDone;
  1203.             END;
  1204.         ELSE        (* passive open not completed or completed in error: error *)
  1205.             L.listening := FALSE;
  1206.             TCPRelease(NIL, L);
  1207.             res := NotDone;
  1208.         END;
  1209.     ELSE    (* no passive open in progress: error *)
  1210.         res := NotDone;
  1211.     END;
  1212. END Accept;
  1213. (** ---------- Connection ---------- *)
  1214. (** Connections are opened with Connect. Reading and writing operations are blocking. Available returns the number of bytes that can be read from a connection without blocking. AvailToSend returns the number of bytes that can be sent over a connection without blocking. Connected is TRUE as long as data can be sent over a connection. A connection is closed with Disconnect.
  1215. A connection has an associated id number that can be used to refer to a connection (field id). ThisConnection maps id numbers to connection objects. Clients that use Oberon tasks should store the connection id in the task, rather than the connection object proper, to give the garbage collector a chance to collect and possibly finalize connections. It is guaranteed that the same id is never allocated for two different connections during one session. *)
  1216. PROCEDURE Connect* (C: Connection; lport: INTEGER; Adr: IpAdr; rport: INTEGER; timeout: LONGINT; VAR res: INTEGER);
  1217.         (** Opens a connection to a peer specified by the pair (Adr, rport). If the peer does not accept the
  1218.             connection after timeout milliseconds, the result code Timeout is returned in res. If  timeout = 0,
  1219.             a default timeout is used.
  1220.             If a specific local port is desired, it can be specified in lport. Using AnyPort for lport means that no special
  1221.             port number is desired for the local port.
  1222.             Adr must be # AnyAdr; rport must be # AnyPort; lport may be AnyAdr or not. *)(*
  1223.             Features retry (if both partners are clients and servers (using different Ports per Connection) and both try to connect
  1224.             at exactly the same moment, TCPActiveOpen may fail). *)
  1225. VAR t, u: LONGINT; i, retries: INTEGER;
  1226. BEGIN
  1227.     ASSERT (Adr # 0); ASSERT (rport # 0);
  1228.     i := FreeConnTabEntry();
  1229.     IF (ioCRefNum = 0) OR (i < 0) THEN res := NotDone; RETURN END;
  1230.     TCPCreate(C, NIL, recBufferSize, O.Time(), i);
  1231.     IF connTab[i] = 0 THEN res := NotDone; RETURN END;
  1232.     IF timeout <= 0 THEN timeout := DefaultConnectTimeout * 1000 (* timeout in milliseconds *)
  1233.     ELSIF timeout <= 1000 THEN timeout := 1000
  1234.     ELSIF timeout > MAX (LONGINT) DIV Sec THEN timeout := MAX (LONGINT) DIV Sec
  1235.     END;
  1236.     t := O.Time(); retries := 3;
  1237.     LOOP
  1238.         TCPOpen(C.pbs, Adr, lport, rport, timeout DIV 1000); (* always timeout > 0: active open, asynchronous *)
  1239.         IF C.pbs.res = noErr THEN
  1240.             C.lport := C.pbs.OpenPb.localPort; C.radr := C.pbs.OpenPb.remAdr; C.rport := C.pbs.OpenPb.remPort;
  1241.             res := Done; RETURN;
  1242.         ELSE
  1243.             IF (C.pbs.res = openFailed) OR (C.pbs.res = commandTimeout) THEN
  1244.                 IF O.Time () - t >= ((timeout - 600) * Sec) DIV 1000 THEN (* not enough time for another retry *)
  1245.                     res := Timeout; EXIT;
  1246.                 ELSIF retries = 0 THEN    (* no more retries even if not yet timeout *)
  1247.                     res := NotDone; EXIT;
  1248.                 ELSE    (* retry if not yet timeout-600ms; random wait 200ms..400ms (200ms = 1sec DIV 5) *)
  1249.                     u := O.Time ();
  1250.                     WHILE O.Time () - u < Sec DIV 5 + u MOD (Sec DIV 5) DO END;
  1251.                     DEC (retries);
  1252.                 END;
  1253.             ELSIF C.pbs.res = duplicateSocket THEN res := LocalPortInUse; EXIT;
  1254.             ELSE res := NotDone; EXIT;
  1255.             END;
  1256.         END;
  1257.     END;
  1258.     TCPRelease(C, NIL); (* res # Done *)
  1259. END Connect;
  1260. PROCEDURE Disconnect* (C: Connection);
  1261.         (** Closes an open connection. *)
  1262. BEGIN
  1263.     TCPStatus(C.pbs);
  1264.     IF C.pbs.res = noErr THEN TCPClose(C.pbs) END;
  1265.     TCPRelease(C, NIL);
  1266. END Disconnect;
  1267. PROCEDURE Connected* (C: Connection): BOOLEAN;
  1268.         (** Returns TRUE if the connection C is writeable. *)
  1269. BEGIN
  1270.     TCPStatus(C.pbs);
  1271.     RETURN (C.pbs.res = noErr) & (C.pbs.StatusPb.connState # Closed);
  1272. END Connected;
  1273. PROCEDURE Available* (C: Connection): LONGINT;
  1274.         (** Returns the number of bytes that can be read on C without blocking. *)
  1275. BEGIN
  1276.     TCPStatus(C.pbs);
  1277.     IF C.pbs.res = noErr THEN
  1278.         RETURN C.pbs.StatusPb.unreadData; (* assume <= MAX (LONGINT); should be 32-bit cardinal *)
  1279.     ELSE
  1280.         RETURN 0;
  1281.     END;
  1282. END Available;
  1283. PROCEDURE AvailToSend* (C: Connection): LONGINT;
  1284.         (** Returns the number of bytes that can be sent on C without blocking. *)
  1285. BEGIN
  1286.     TCPStatus(C.pbs);
  1287.     IF C.pbs.res = noErr THEN
  1288.         RETURN C.pbs.StatusPb.sndWindow; (* assume <= MAX (LONGINT) *)
  1289.         (* should be C.pbs.StatusPb.sndWindow - ABS (C.pbs.StatusPb.sndNext - C.pbs.StatusPb.sndUnacknowledged)
  1290.             using 32-bit arithmetic (32-bit cardinals); rfc793, p. 20; ABS for safety *)
  1291.     ELSE
  1292.         RETURN 0;
  1293.     END;
  1294. END AvailToSend;
  1295. PROCEDURE ThisConnection* (id: LONGINT): Connection;
  1296.         (** Returns connection with C.id = id, or NIL if no such connection exists. *)
  1297. VAR i: INTEGER;
  1298. BEGIN
  1299.     i := ThisConn(id);
  1300.     IF i >= 0 THEN
  1301.         IF connTab[i] = 0 THEN
  1302.             RETURN NIL
  1303.         ELSE
  1304.             RETURN SYSTEM.VAL(Connection, connTab[i]);
  1305.         END;
  1306.     ELSE
  1307.         RETURN NIL
  1308.     END;
  1309. END ThisConnection;
  1310. PROCEDURE ThisListener* (id: LONGINT): Listener;
  1311.         (** Mac only: Returns listener with L.id = id, or NIL if no such listener exists. *)
  1312. VAR i: INTEGER;
  1313. BEGIN
  1314.     i := ThisList(id);
  1315.     IF i >= 0 THEN
  1316.         IF listTab[i] = 0 THEN
  1317.             RETURN NIL
  1318.         ELSE
  1319.             RETURN SYSTEM.VAL(Listener, listTab[i]);
  1320.         END;
  1321.     ELSE
  1322.         RETURN NIL
  1323.     END;
  1324. END ThisListener;
  1325. (* ---------- Data Formats ---------- *)
  1326. (*    Data formats for interpreted data exchange are
  1327.     Type               Format
  1328.     INTEGER         two bytes, network byte ordering
  1329.     LONGINT        four bytes, network byte ordering
  1330.     REAL              four bytes, IEEE single precision
  1331.     LONGREAL     eight bytes, IEEE double precision
  1332.     SET                four bytes, SYSTEM.VAL(LONGINT, {0}) = 1
  1333.     BOOLEAN      one byte
  1334. (** ---------- Reading ---------- *)
  1335. (** All Read* procedures are blocking.
  1336.     Mac: All Read* procedures are blocking if connection is alive, return undefined values immediately if connection is not alive. *)
  1337. PROCEDURE Read* (C: Connection; VAR x: SYSTEM.BYTE);
  1338. VAR rlen: INTEGER;
  1339. BEGIN
  1340.     rlen := 1; TCPRcv (C, SYSTEM.ADR(x), rlen);
  1341. END Read;
  1342. PROCEDURE ReadBool* (C: Connection; VAR b: BOOLEAN);
  1343. VAR rlen: INTEGER; c: CHAR;
  1344. BEGIN
  1345.     rlen := 1; TCPRcv (C, SYSTEM.ADR(c), rlen); b := c # 0X;
  1346. END ReadBool;
  1347. PROCEDURE ReadBytes* (C: Connection; VAR x: ARRAY OF SYSTEM.BYTE; beg, len: LONGINT);
  1348.     (** Reads bytes x[beg] .. x[beg+len-1];  Precondition: (beg >= 0) AND (beg+len <= LEN(x)) *)
  1349. VAR rlen, rcvlen, pos: INTEGER;
  1350. BEGIN
  1351.     (* ASSERT((beg >= 0) & (LEN(x) >= beg+len)); *)
  1352.     pos := 0;
  1353.     REPEAT
  1354.         IF len > MAX (INTEGER) THEN rlen := MAX (INTEGER) ELSE rlen := SHORT (len) END;
  1355.         rcvlen := rlen;
  1356.         TCPRcv (C, SYSTEM.ADR(x) + beg + pos, rcvlen);
  1357.         pos := pos + rcvlen;
  1358.         len := len - rcvlen;
  1359.     UNTIL (rcvlen = 0) OR (len = 0);
  1360. END ReadBytes;
  1361. PROCEDURE ReadInt* (C: Connection; VAR x: INTEGER);
  1362. VAR rlen: INTEGER;
  1363. BEGIN
  1364.     rlen := 2; TCPRcv (C, SYSTEM.ADR(x), rlen);
  1365. END ReadInt;
  1366. PROCEDURE ReadLInt* (C: Connection; VAR x: LONGINT);
  1367. VAR rlen: INTEGER;
  1368. BEGIN
  1369.     rlen := 4; TCPRcv (C, SYSTEM.ADR(x), rlen);
  1370. END ReadLInt;
  1371. PROCEDURE ReadLReal* (C: Connection; VAR r: LONGREAL);
  1372. VAR rlen: INTEGER;
  1373. BEGIN
  1374.     rlen := 8; TCPRcv (C, SYSTEM.ADR(r), rlen);
  1375. END ReadLReal;
  1376. PROCEDURE ReadReal* (C: Connection; VAR r: REAL);
  1377. VAR rlen: INTEGER;
  1378. BEGIN
  1379.     rlen := 4; TCPRcv (C, SYSTEM.ADR(r), rlen);
  1380. END ReadReal;
  1381. PROCEDURE ReadSet* (C: Connection; VAR s: SET);
  1382. VAR rlen, i: INTEGER; l: LONGINT;
  1383. BEGIN
  1384.     rlen := 4; TCPRcv (C, SYSTEM.ADR(l), rlen);
  1385.     s := {}; FOR i := 0 TO 31 DO IF ODD (l) THEN INCL (s, i) END; l := ASH (l, -1) END; (* reverse bit order *)
  1386. END ReadSet;
  1387. PROCEDURE ReadString* (C: Connection; VAR s: ARRAY OF CHAR);
  1388.     (** Reads a string which is terminated by 0X. *)
  1389. VAR rlen, i: INTEGER;
  1390. BEGIN i := -1;
  1391.     REPEAT rlen := 1; INC (i);
  1392.         TCPRcv (C, SYSTEM.ADR(s[i]), rlen);
  1393.     UNTIL (rlen = 0) OR (s[i] = 0X);
  1394. END ReadString;
  1395. PROCEDURE ReadLine* (C: Connection; VAR s: ARRAY OF CHAR);
  1396.     (** Reads a line which is terminated by ["CR"]"LF". *)
  1397. VAR rlen, i: INTEGER;
  1398. BEGIN i := -1;
  1399.     REPEAT rlen := 1; INC (i);
  1400.         TCPRcv (C, SYSTEM.ADR(s[i]), rlen);
  1401.     UNTIL (rlen = 0) OR (s[i] = 0AX);
  1402. END ReadLine;
  1403. (** ---------- Writing ---------- *)
  1404. (** All Write* procedures are blocking.
  1405.     Mac: All Write* procedures are blocking if connection is alive, return immediately if connection is not alive. *)
  1406. PROCEDURE Write* (C: Connection; x: SYSTEM.BYTE);
  1407. BEGIN
  1408.     TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(x)), 1);
  1409. END Write;
  1410. PROCEDURE WriteBool* (C: Connection; b: BOOLEAN);
  1411. VAR c: CHAR;
  1412. BEGIN
  1413.     IF b THEN c := 0FFX ELSE c := 0X END; TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(c)), 1);
  1414. END WriteBool;
  1415. PROCEDURE WriteBytes* (C: Connection; VAR x: ARRAY OF SYSTEM.BYTE; beg, len: LONGINT);
  1416.     (** Writes bytes x[beg] .. x[beg+len-1];  Precondition: (beg >= 0) AND (beg+len <= LEN(x)) *)
  1417. VAR wlen, pos: INTEGER;
  1418. BEGIN
  1419.     (* ASSERT((beg >= 0) & (LEN(x) >= beg + len)); *)
  1420.     pos := 0;
  1421.     REPEAT
  1422.         IF len > MAX (INTEGER) THEN wlen := MAX (INTEGER) ELSE wlen := SHORT (len) END;
  1423.         TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(x) + beg + pos), wlen);
  1424.         pos := pos + wlen;
  1425.         len := len - wlen;
  1426.     UNTIL (*(wlen = 0) OR*) (res # Done) OR (len = 0);
  1427. END WriteBytes;
  1428. PROCEDURE WriteInt* (C: Connection; x: INTEGER);
  1429. BEGIN
  1430.     TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(x)), 2);
  1431. END WriteInt;
  1432. PROCEDURE WriteLInt* (C: Connection; x: LONGINT);
  1433. BEGIN
  1434.     TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(x)), 4);
  1435. END WriteLInt;
  1436. PROCEDURE WriteLReal* (C: Connection; r: LONGREAL);
  1437. BEGIN
  1438.     TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(r)), 8);
  1439. END WriteLReal;
  1440. PROCEDURE WriteReal* (C: Connection; r: REAL);
  1441. BEGIN
  1442.     TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(r)), 4);
  1443. END WriteReal;
  1444. PROCEDURE WriteSet* (C: Connection; s: SET);
  1445. VAR i: INTEGER; l: LONGINT;
  1446. BEGIN
  1447.     l := 0; FOR i := 31 TO 0 BY -1 DO l := ASH (l, 1); IF i IN s THEN INC (l) END END; (* reverse bit order *)
  1448.     TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(l)), 4);
  1449. END WriteSet;
  1450. PROCEDURE WriteString* (C: Connection; s: ARRAY OF CHAR);
  1451.     (** Writes a string terminated by 0X. *)
  1452. VAR i: INTEGER;
  1453. BEGIN
  1454.         i := 0; WHILE s[i] # 0X DO INC (i) END;
  1455.         TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(s)), i + 1);
  1456. END WriteString;
  1457. PROCEDURE WriteLine* (C: Connection; s: ARRAY OF CHAR);
  1458.     (** Writes a line without 0X and terminated by ["CR"]"LF". *)
  1459. VAR i: INTEGER;
  1460. BEGIN
  1461.         i := 0; WHILE s[i] # 0AX DO INC (i) END;
  1462.         TCPSend (C, SYSTEM.VAL(BufferPtr, SYSTEM.ADR(s)), i + 1);
  1463. END WriteLine;
  1464. (* ---------- Initialization ---------- *)
  1465. BEGIN
  1466.     T.OpenWriter (W);
  1467.     T.WriteString (W, "TCP for PowerMac Oberon"); T.WriteLn (W); T.Append (O.Log, W.buf);
  1468. (* DNR *)
  1469.     addrCache := 0; shortNameCache[0] := 0X; longNameCache[0] := 0X;
  1470.     dnr := NIL;
  1471.     oberonIoVRefNum := 0;
  1472.     oberonDirID := 0;
  1473.     NEW (gRtnStruct);
  1474. (* TCP *)
  1475.     FOR i := 0 TO MaxStreams - 1 DO connTab[i] := 0; listTab[i] := 0 END; connCount := 0; listCount := 0;
  1476.     ioCRefNum := 0;
  1477.     Sys.Assign ("NewPtr", SYSTEM.ADR (NewPtr)); ASSERT(NewPtr # NIL);
  1478.     Sys.Assign ("DisposePtr", SYSTEM.ADR (DisposePtr)); ASSERT(DisposePtr # NIL);
  1479.     Sys.Assign ("PBOpenSync", SYSTEM.ADR (PBOpenSync)); ASSERT(PBOpenSync # NIL);
  1480.     Sys.Assign ("PBControlSync", SYSTEM.ADR (PBControlSync)); ASSERT(PBControlSync # NIL);
  1481.     Sys.Assign ("PBControlAsync", SYSTEM.ADR (PBControlAsync)); ASSERT(PBControlAsync # NIL);
  1482. (* DNR *)
  1483.     Sys.Assign ("PBHGetFInfoSync", SYSTEM.ADR (PBHGetFInfoSync)); ASSERT(PBHGetFInfoSync # NIL);
  1484.     Sys.Assign ("HGetVol", SYSTEM.ADR (HGetVol)); ASSERT(HGetVol # NIL);
  1485.     Sys.Assign ("HSetVol", SYSTEM.ADR (HSetVol)); ASSERT(HSetVol # NIL);
  1486.     Sys.Assign ("DisposeHandle", SYSTEM.ADR (DisposeHandle)); ASSERT(DisposeHandle # NIL);
  1487.     Sys.Assign ("HLock", SYSTEM.ADR (HLock)); ASSERT(HLock # NIL);
  1488.     Sys.Assign ("HUnlock", SYSTEM.ADR (HUnlock)); ASSERT(HUnlock # NIL);
  1489.     Sys.Assign ("OpenResFile", SYSTEM.ADR (OpenResFile)); ASSERT(OpenResFile # NIL);
  1490.     Sys.Assign ("CloseResFile", SYSTEM.ADR (CloseResFile)); ASSERT(CloseResFile # NIL);
  1491.     Sys.Assign ("ResError", SYSTEM.ADR (ResError)); ASSERT(ResError # NIL);
  1492.     Sys.Assign ("GetIndResource", SYSTEM.ADR (GetIndResource)); ASSERT(GetIndResource # NIL);
  1493.     Sys.Assign ("DetachResource", SYSTEM.ADR (DetachResource)); ASSERT(DetachResource # NIL);
  1494.     Sys.Assign ("FindFolder", SYSTEM.ADR (FindFolder)); ASSERT(FindFolder # NIL);
  1495. (* TCP *)
  1496.     InitDriver; res := Done;
  1497. END TCP.
  1498.